View Javadoc
1   package org.apache.maven.surefire.testng.conf;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.lang.reflect.Method;
23  import java.util.ArrayList;
24  import java.util.HashMap;
25  import java.util.Map;
26  
27  import org.apache.maven.surefire.testset.TestSetFailedException;
28  import org.testng.TestNG;
29  import org.testng.xml.XmlSuite;
30  
31  import static java.lang.Integer.parseInt;
32  import static org.apache.maven.surefire.booter.ProviderParameterNames.PARALLEL_PROP;
33  import static org.apache.maven.surefire.booter.ProviderParameterNames.THREADCOUNT_PROP;
34  import static org.apache.maven.surefire.testng.conf.AbstractDirectConfigurator.loadListenerClasses;
35  
36  /**
37   * TestNG configurator for 5.3+ versions. TestNG exposes a {@link org.testng.TestNG#configure(java.util.Map)} method.
38   * All suppported TestNG options are passed in String format, except
39   * <code>TestNGCommandLineArgs.LISTENER_COMMAND_OPT</code> which is <code>List&gt;Class&lt;</code>,
40   * <code>TestNGCommandLineArgs.JUNIT_DEF_OPT</code> which is a <code>Boolean</code>,
41   * <code>TestNGCommandLineArgs.SKIP_FAILED_INVOCATION_COUNT_OPT</code> which is a <code>Boolean</code>,
42   * <code>TestNGCommandLineArgs.OBJECT_FACTORY_COMMAND_OPT</code> which is a <code>Class</code>,
43   * <code>TestNGCommandLineArgs.REPORTERS_LIST</code> which is a <code>List&gt;ReporterConfig&lt;</code>.
44   * <p/>
45   * Test classes and/or suite files are not passed along as options parameters, but configured separately.
46   *
47   * @author <a href='mailto:the[dot]mindstorm[at]gmail[dot]com'>Alex Popescu</a>
48   */
49  public class TestNGMapConfigurator
50      implements Configurator
51  {
52      public void configure( TestNG testng, Map<String, String> options )
53          throws TestSetFailedException
54      {
55          Map convertedOptions = getConvertedOptions( options );
56          testng.configure( convertedOptions );
57      }
58  
59      public void configure( XmlSuite suite, Map<String, String> options )
60          throws TestSetFailedException
61      {
62          String threadCountAsString = options.get( THREADCOUNT_PROP );
63          int threadCount = threadCountAsString == null ? 1 : parseInt( threadCountAsString );
64          suite.setThreadCount( threadCount );
65  
66          String parallel = options.get( PARALLEL_PROP );
67          if ( parallel != null )
68          {
69              suite.setParallel( parallel );
70          }
71      }
72  
73      Map<String, Object> getConvertedOptions( Map<String, String> options )
74          throws TestSetFailedException
75      {
76          Map<String, Object> convertedOptions = new HashMap<String, Object>();
77          convertedOptions.put( "-mixed", false );
78          for ( Map.Entry<String, String> entry : options.entrySet() )
79          {
80              String key = entry.getKey();
81              Object val = entry.getValue();
82              // todo: use switch-case of jdk7
83              if ( "listener".equals( key ) )
84              {
85                  val = convertListeners( entry.getValue() );
86              }
87              else if ( "objectfactory".equals( key ) )
88              {
89                  val = AbstractDirectConfigurator.loadClass( entry.getValue() );
90              }
91              else if ( "testrunfactory".equals( key ) )
92              {
93                  val = AbstractDirectConfigurator.loadClass( entry.getValue() );
94              }
95              else if ( "reporter".equals( key ) )
96              {
97                  // for TestNG 5.6 or higher
98                  // TODO support multiple reporters?
99                  val = convertReporterConfig( val );
100                 key = "reporterslist";
101             }
102             else if ( "junit".equals( key ) )
103             {
104                 val = convert( val, Boolean.class );
105             }
106             else if ( "skipfailedinvocationcounts".equals( key ) )
107             {
108                 val = convert( val, Boolean.class );
109             }
110             else if ( "mixed".equals( key ) )
111             {
112                 val = convert( val, Boolean.class );
113             }
114             else if ( "configfailurepolicy".equals( key ) )
115             {
116                 val = convert( val, String.class );
117             }
118             else if ( "group-by-instances".equals( key ) )
119             {
120                 val = convert( val, Boolean.class );
121             }
122             else if ( THREADCOUNT_PROP.equals( key ) )
123             {
124                 val = convert ( val, String.class );
125             }
126             else if ( "suitethreadpoolsize".equals( key ) )
127             {
128                 // for TestNG 6.9.7 or higher
129                 val = convert( val, Integer.class );
130             }
131             else if ( "dataproviderthreadcount".equals( key ) )
132             {
133                 // for TestNG 5.10 or higher
134                 val = convert( val, Integer.class );
135             }
136 
137             if ( key.startsWith( "-" ) )
138             {
139                 convertedOptions.put( key, val );
140             }
141             else
142             {
143                 convertedOptions.put( "-" + key, val );
144             }
145         }
146         return convertedOptions;
147     }
148 
149     // ReporterConfig only became available in later versions of TestNG
150     protected Object convertReporterConfig( Object val )
151     {
152         try
153         {
154             Class<?> reporterConfig = Class.forName( "org.testng.ReporterConfig" );
155             Method deserialize = reporterConfig.getMethod( "deserialize", String.class );
156             Object rc = deserialize.invoke( null, val );
157             ArrayList<Object> reportersList = new ArrayList<Object>();
158             reportersList.add( rc );
159             return reportersList;
160         }
161         catch ( Exception e )
162         {
163             return val;
164         }
165     }
166 
167     protected Object convertListeners( String listenerClasses ) throws TestSetFailedException
168     {
169         return loadListenerClasses( listenerClasses );
170     }
171 
172     protected Object convert( Object val, Class<?> type )
173     {
174         if ( val == null )
175         {
176             return null;
177         }
178         else if ( type.isAssignableFrom( val.getClass() ) )
179         {
180             return val;
181         }
182         else if ( ( type == Boolean.class || type == boolean.class ) && val.getClass() == String.class )
183         {
184             return Boolean.valueOf( (String) val );
185         }
186         else if ( ( type == Integer.class || type == int.class ) && val.getClass() == String.class )
187         {
188             return Integer.valueOf( (String) val );
189         }
190         else if ( type == String.class )
191         {
192             return val.toString();
193         }
194         else
195         {
196             return val;
197         }
198     }
199 }